package me.sluijsens.StartersQuiz;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.permission.Permission;

import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

/**
 * Somewhat the core of the plugin. This handles all the quiz actions.
 * Show or answer the question, retry, start, everything!
 * @author Bryan Slop (Sluijsens)
 *
 */
public class QuizHandler {
	private StartersQuiz plugin;
	private List<String> question_lines;
	private String[] question_arr;
	private String question;
	private ConfigHandler conf;
	private Map<String, ConfigHandler> qConf;
	private Map<Player, ConfigHandler> pConf;
	private LangHandler lang;
	private String tagChat;
	
	private Map<Player, Integer> atQ = null;
	private Map<Player, String> atQuiz = null;
	private Map<Player, Boolean> finished = null;
	private Map<Player, Boolean> started = null;
	private Map<Player, Integer> attempt = null;
	private Map<Player, List<Integer>> nrsQuestions = null;
	
	private Permission permission;
	private Economy economy;
	
	public List<World> worlds;
	public List<String> world_names;
	
	/**
	 * Get instance and get the worlds and their names
	 * @param instance Instance of the plugin
	 */
	public QuizHandler(StartersQuiz instance) {
		plugin = instance;
		
		worlds = plugin.getServer().getWorlds();
		world_names = new ArrayList<String>();
		
		for(int i = 0; i < worlds.size(); i++) {
			world_names.add(worlds.get(i).getName());
		}
	}
	
	/**
	 * Start the quiz
	 * @param player Player who starts the quiz
	 */
	@SuppressWarnings("static-access")
	public void start(Player player) {
		lang = plugin.getLangHandler();
		tagChat = plugin.tagChat;
		started = plugin.started;
		pConf = plugin.getPConf();
		atQuiz = plugin.atQuiz;
		nrsQuestions = plugin.nrsQuestions;
		qConf = plugin.getQConf();
		
		String quiz = atQuiz.get(player);
		int atQuestion = pConf.get(player).getInt(quiz + ".AtQuestion");
		
		if(plugin.checkDone(player)) player.sendMessage(tagChat + lang.getMessage("StartError"));
		else {
			if(qConf.get(quiz).isSet("PickFromPool") && qConf.get(quiz).getInt("PickFromPool", 0) > 0 && atQuestion <= 1 || qConf.get(quiz).getBoolean("Shuffle")) {
				// First check if there are less questions than Set in PickFromPool, if so set amount to amount of questions if not set to configured amount
				question_lines = (List<String>) qConf.get(quiz).getList("Quiz");
				int amount = 0;
				if(question_lines.size() < qConf.get(quiz).getInt("PickFromPool") || qConf.get(quiz).getInt("PickFromPool") < 1 && qConf.get(quiz).getBoolean("Shuffle")) amount = question_lines.size();
				else amount = qConf.get(quiz).getInt("PickFromPool");
				
				List<Integer> listNrs =  new ArrayList<Integer>();
				int[] arrNrs = new int[amount];
				int i = 0;
				while(i < amount) {
					double rand = Math.random() * (double) question_lines.size();
					
					int nr = (int) Math.floor(rand);
					if(!listNrs.contains(nr)) {
						listNrs.add(nr);
						arrNrs[i] = nr;
						i++;
					}
					
				}
				
				nrsQuestions.put(player, listNrs);
				
				if(!qConf.get(quiz).getBoolean("Shuffle", false)) {
					arrNrs = plugin.sort(arrNrs);
					listNrs =  new ArrayList<Integer>();
					
					for(int nr: arrNrs) {
						listNrs.add(nr);
					}
				}
				
				pConf.get(player).set(quiz + ".PoolQuestions", listNrs);
			}
			started.put(player, true);
			pConf.get(player).set(quiz + ".Started", started.get(player));
			
			player.sendMessage(tagChat + lang.getMessage("Start"));
			
			pConf.get(player).save();
		}
	}
	
	/**
	 * Show the current question to the user
	 * @param player Player to show the question to
	 */
	@SuppressWarnings({ "unchecked", "static-access" })
	public void show(Player player) {
		atQ = plugin.atQ;
		atQuiz = plugin.atQuiz;
		tagChat = plugin.tagChat;
		qConf = plugin.getQConf();
		started = plugin.started;
		
		if(!plugin.checkDone(player) && started.get(player)) {
			String quiz = atQuiz.get(player);
			int playerQ = atQ.get(player) - 1;
			
			if(pConf.get(player).isSet(quiz + ".PoolQuestions"))  {
				List<Integer> nrsQs = (List<Integer>) pConf.get(player).getList(quiz + ".PoolQuestions");
				playerQ = nrsQs.get(playerQ);
			}
			
			question_lines = (List<String>) qConf.get(quiz).getList("Quiz");
			question_arr = question_lines.get(playerQ).split(",");
			
			question = tagChat + question_arr[0];
			player.sendMessage(question);
			
			String answer;
			for(int i = 2; i < question_arr.length; i++) {
				answer = question_arr[i];
				player.sendMessage("- " + answer);
			}
		}
	}
	
	/**
	 * Answer a question
	 * @param answer Answer that has been given by the player
	 * @param player The player who answers the question
	 */
	@SuppressWarnings({ "unchecked", "static-access" })
	public void answer(String answer, Player player) {
		atQ = plugin.atQ;
		atQuiz = plugin.atQuiz;
		tagChat = plugin.tagChat;
		qConf = plugin.getQConf();
		pConf = plugin.getPConf();
		lang = plugin.getLangHandler();
		started = plugin.started;
		
		if(!plugin.checkDone(player)) {
			String quiz = atQuiz.get(player);
			List<String> answers = (List<String>) pConf.get(player).getList(quiz + ".Answers");
			List<Integer> nrsQs = new ArrayList<Integer>();
			
			if(pConf.get(player).isSet(quiz + ".PoolQuestions")) nrsQs = (List<Integer>) pConf.get(player).getList(quiz + ".PoolQuestions");
			
			question_lines = (List<String>) qConf.get(quiz).getList("Quiz");
		
			answers.add(answer);
			pConf.get(player).set(quiz + ".Answers", answers);
		
			atQ.put(player, atQ.get(player) + 1);
			pConf.get(player).set(quiz + ".AtQuestion", atQ.get(player));
			
			if(answers.size() >= question_lines.size() || answers.size() >= nrsQs.size()) {
				if(check(player)) passed(player); // Player passed the quiz
				else failed(player); // Player did not pass the quiz
			} else {
				show(player);
			}
		
			pConf.get(player).save();
		} else player.sendMessage(tagChat + lang.getMessage("AnswerError"));
	}
	
	
	/**
	 * Retry a quiz and start all over
	 * @param player Player that retries the quiz
	 * @param force Force the retry, if you do not force there is a chance that it does not retry
	 */
	@SuppressWarnings("static-access")
	public void retry(Player player, boolean force) {
		atQuiz = plugin.atQuiz;
		qConf = plugin.getQConf();
		pConf = plugin.getPConf();
		conf = plugin.getConf();
		attempt = plugin.attempt;
		tagChat = plugin.tagChat;
		lang = plugin.getLangHandler();
		started = plugin.started;
		
		if(!plugin.checkDone(player)) {
			String quiz = atQuiz.get(player);
			int maxAttempts = qConf.get(quiz).getInt("MaxAttempts", 3);
			int nrAttempt = attempt.get(player);
			ArrayList<String> answers = new ArrayList<String>();
			
			if(nrAttempt < maxAttempts || force) {
				String confBan;
				if ((qConf.get(quiz).isSet("TimeOfBan") && qConf.get(quiz).getString("TimeOfBan").split(",").length > 1 ||
						conf.isSet("TimeOfBan") && conf.getString("TimeOfBan").split(",").length > 1) && maxAttempts == 1) {
					
					if (qConf.get(quiz).isSet("TimeOfBan") && qConf.get(quiz).getString("TimeOfBan").split(",").length > 1) confBan = qConf.get(quiz).getString("TimeOfBan");
					else confBan = conf.getString("TimeOfBan");
						
					force = false;
					
					if(nrAttempt >= confBan.split(",").length) {
						nrAttempt = confBan.split(",").length - 1;
					}
				}
				if(force) {
					attempt.put(player, 1);
				} else {
					attempt.put(player, nrAttempt + 1);
				}
				
				pConf.get(player).set(quiz + ".Attempt", attempt.get(player));
				pConf.get(player).set(quiz + ".Answers", answers);
				
				atQ.put(player, 1);
				pConf.get(player).set(quiz + ".AtQuestion", 1);
				
				player.sendMessage(tagChat + lang.getMessage("Retry"));
			} else {
				player.sendMessage(tagChat + lang.getMessage("RetryErrorMaxAttempts"));
			}
			
			pConf.get(player).save();
		} else {
			player.sendMessage(tagChat + lang.getMessage("RetryError"));
		}
	}
	
	
	/**
	 * Check if the player passed the quiz or not
	 * @param player Player to check
	 * @return Return true if the player passed
	 */
	@SuppressWarnings({ "unchecked", "static-access" })
	public boolean check(Player player) {
		atQuiz = plugin.atQuiz;
		pConf = plugin.getPConf();
		qConf = plugin.getQConf();
		
		String quiz = atQuiz.get(player);
		int playerQ = atQ.get(player) - 1;
		
		List<String> answers = (List<String>) pConf.get(player).getList(quiz + ".Answers");
		List<String> rightAnswers = new ArrayList<String>();
		
		if(pConf.get(player).isSet(quiz + ".PoolQuestions")) {
			List<Integer> nrsQs = (List<Integer>) pConf.get(player).getList(quiz + ".PoolQuestions");
			List<String> allQuestions = (List<String>) qConf.get(quiz).getList("Quiz");
			
			question_lines = new ArrayList();
			for(int q: nrsQs) {
				question_lines.add(allQuestions.get(q));
			}
		} else question_lines = (List<String>) qConf.get(quiz).getList("Quiz");
		
		for(int i = 0; i < question_lines.size(); i++) {
			question_arr = question_lines.get(i).split(",");
			rightAnswers.add(question_arr[1]);
		}
		int total = rightAnswers.size();
		
		int percentage = qConf.get(quiz).getInt("PercentageToPass");
		int right = 0;
		
		for(int i = 0; i < total; i++) {
			if(answers.get(i).equalsIgnoreCase(rightAnswers.get(i))) {
				right++;
			}
		}
		
		double score = Math.round((((double)right/(double)total) * 100));
		if(score >= percentage) return true;
		else return false;
	}
	
	/**
	 * Player passed so end his quiz and do the necessary things. Give rewards and/or promote to group and/or give/remove permissions.
	 * @param player Player to congratulate
	 */
	@SuppressWarnings({ "unchecked", "static-access" })
	public void passed(Player player) {
		finished = plugin.finished;
		started = plugin.started;
		atQuiz = plugin.atQuiz;
		permission = plugin.permission;
		economy = plugin.economy;
		qConf = plugin.getQConf();
		tagChat = plugin.tagChat;
		lang = plugin.getLangHandler();
		
		List<String> permissions;
		String quiz = atQuiz.get(player);
		
		finished.put(player, true);
		pConf.get(player).set(quiz + ".Finished", finished.get(player));
		
		started.put(player, false);
		pConf.get(player).set(quiz + ".Started", started.get(player));
		
		if(qConf.get(quiz).isSet("PromoteToGroup")) {
			String group = qConf.get(quiz).getString("PromoteToGroup");
			
			//Groupmanager needs its own mirroring or StartersQuiz need quiz per world
			if(plugin.getServer().getPluginManager().getPlugin("GroupManager") != null) {
				permission.playerRemoveGroup(player, permission.getPrimaryGroup(player));
				permission.playerAddGroup(player, group);
			} else {
				player.sendMessage("GroupManager disabled");
				for(String w: world_names) {
					permission.playerRemoveGroup(w, player.getDisplayName(), permission.getPrimaryGroup(w, player.getDisplayName()));
					permission.playerAddGroup(w, player.getDisplayName(), group);
				}
			}
		}
		if(qConf.get(quiz).isSet("RemovePermissions")) {
			permissions = (List<String>) qConf.get(quiz).getList("RemovePermissions");
			
			for(String p: permissions) {
				for(String w: world_names) {
					if(permission.has(w, player.getDisplayName(), p)) permission.playerRemove(w, player.getDisplayName(), p);
				}
			}
		}
		if(qConf.get(quiz).isSet("AddPermissions")) {
			permissions = (List<String>) qConf.get(quiz).getList("AddPermissions");
			
			for(String p: permissions) {
				for(String w: world_names) {
					if(!permission.has(w, player.getDisplayName(), p)) permission.playerAdd(w, player.getDisplayName(), p);
				}
			}
		}
		if(qConf.get(quiz).isSet("RewardItems")) {
			String[] rewards = qConf.get(quiz).getString("RewardItems").split(",");
			
			for(int i = 0; i < rewards.length; i++) {
				String[] reward = rewards[i].split("x");
				
				int amount = Integer.parseInt(reward[0]);
				int item = 0;
				byte itemdata = 0;
				if(reward[1].contains(":")) {
					String[] infoItem = reward[1].split(":");
					item = Integer.parseInt(infoItem[0]);
					itemdata = Byte.parseByte(infoItem[1]);
				} else item = Integer.parseInt(reward[1]);
			
				ItemStack is = new ItemStack(item, amount, (short) 0, itemdata);
				player.getInventory().addItem(is);
			}
		}
		
		if(qConf.get(quiz).isSet("RewardMoney")) {
			double amount = qConf.get(quiz).getDouble("RewardMoney");
			economy.depositPlayer(player.getName(), amount);
		}
		
		player.sendMessage(tagChat + lang.getMessage("Passed"));
		
		pConf.get(player).save();
	}
	
	/**
	 * Player did not pass the quiz. Let him retry or ban the player
	 * @param player Player who failed
	 */
	@SuppressWarnings({ "static-access"})
	public void failed(Player player) {
		atQuiz = plugin.atQuiz;
		conf = plugin.getConf();
		qConf = plugin.getQConf();
		pConf = plugin.getPConf();
		tagChat = plugin.tagChat;
		lang = plugin.getLangHandler();
		attempt = plugin.attempt;
		
		String quiz = atQuiz.get(player);
		
		String[] confBan = null;
		if(qConf.get(quiz).isSet("TimeOfBan")) confBan = qConf.get(quiz).getString("TimeOfBan").split(",");
		else if(conf.isSet("TimeOfBan")) confBan = conf.getString("TimeOfBan").split(",");
		else {
			confBan = new String[1];
			confBan[0] = "5m";
		}
		
		long banTime;
		int maxAttempts = qConf.get(quiz).getInt("MaxAttempts", 3);
		int nrAttempt = attempt.get(player);
		Date curDate = new Date();
		long curTime = curDate.getTime();
		
		if(nrAttempt < maxAttempts) {
			player.sendMessage(tagChat + lang.getMessage("Failed"));
			retry(player, false);
		} else {
			String ban;
			if(confBan.length > 1 && nrAttempt <= confBan.length) {
				ban = confBan[nrAttempt - 1];
			} else {
				ban = confBan[0];
			}
			
			if(ban.equalsIgnoreCase("permaban") || ban.equalsIgnoreCase("perm") || ban.equalsIgnoreCase("permanent")) banTime = -1;
			else if(ban.contains("s")) banTime = Integer.parseInt(ban.replace("s", "000"));
			else if(ban.contains("m")) banTime = Integer.parseInt(ban.replace("m", "000")) * 60;
			else if(ban.contains("h")) banTime = Integer.parseInt(ban.replace("h", "000")) * 3600;
			else if(ban.contains("d")) banTime = Integer.parseInt(ban.replace("d", "000")) * 86400;
			else if(ban.contains("w")) banTime = Integer.parseInt(ban.replace("w", "000")) * 604800;
			else banTime = 5 * 6000;
			
			retry(player, true);
			
			if(banTime == -1) {
				pConf.get(player).set("BannedTill", -1);
				player.kickPlayer("You have been permanently banned for failing this quiz too many times.");
			} else {
				pConf.get(player).set("BannedTill", curTime + banTime);
				player.kickPlayer("You have been banned for failing this quiz " + nrAttempt + " times.");
			}
		}
		
		pConf.get(player).save();
	}
}
